package gis.uzd_1;

import java.io.File;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.Vector;

import org.geotools.data.DataUtilities;
import org.geotools.data.FeatureSource;
import org.geotools.data.FileDataStoreFinder;
import org.geotools.factory.CommonFactoryFinder;
import org.geotools.feature.FeatureCollection;
import org.geotools.feature.FeatureCollections;
import org.geotools.feature.FeatureIterator;
import org.geotools.feature.simple.SimpleFeatureBuilder;
import org.geotools.feature.simple.SimpleFeatureTypeBuilder;
import org.geotools.filter.function.StaticGeometry;
import org.geotools.geometry.jts.JTSFactoryFinder;
import org.opengis.feature.simple.SimpleFeature;
import org.opengis.feature.simple.SimpleFeatureType;
import org.opengis.feature.type.FeatureType;
import org.opengis.filter.Filter;
import org.opengis.filter.FilterFactory2;

import com.vividsolutions.jts.geom.Coordinate;
import com.vividsolutions.jts.geom.Geometry;
import com.vividsolutions.jts.geom.GeometryCollection;
import com.vividsolutions.jts.geom.GeometryFactory;
import com.vividsolutions.jts.geom.LinearRing;
import com.vividsolutions.jts.geom.Polygon;
import com.vividsolutions.jts.io.WKTReader;

/**
 * Towers task's logic.
 * 
 * @version 0.1
 * @date 2010-05-14
 * @author Edmundas Matusevicius
 */
public class TowerLogic {
    /*
     * Set up mock up data for development and testing.
     */
    public void setUpMockUpData() {
        try {
            bufferData.add(FileDataStoreFinder.getDataStore(new File("D:\\Edmundas\\Desktop\\lt200shp\\ezerai.shp")).getFeatureSource());
            bufferData.add(FileDataStoreFinder.getDataStore(new File("D:\\Edmundas\\Desktop\\lt200shp\\keliai.shp")).getFeatureSource());
            bufferData.add(FileDataStoreFinder.getDataStore(new File("D:\\Edmundas\\Desktop\\lt200shp\\upes.shp")).getFeatureSource());
            reliefData = FileDataStoreFinder.getDataStore(new File("D:\\Edmundas\\Desktop\\lt200shp\\reljefas.shp")).getFeatureSource();
            crestData = FileDataStoreFinder.getDataStore(new File("D:\\Edmundas\\Desktop\\lt200shp\\virsukal.shp")).getFeatureSource();;
        } catch (Exception e) {
            
        }
        
        objectBufferization = 100;
        wirelessCoverage = 80;
        towerHeight = 200;
        towerRange = 2000;
    }
    
    /*
     * Execute logic calculation.
     */
    public void run() {
        filterData();
        bufferData();
        mergeData();
        
        
        reverseBufferData();
        
        // TODO: TEMPORARY
        // add data to the map
        
//        for (FeatureCollection<SimpleFeatureType, SimpleFeature> otherObject : otherObjects) {
//            GISMap.mapContext.addLayer(otherObject, null);
//        }
        
//        GISMap.mapContext.addLayer(otherObjects.get(2), null);
        
//        showInMap(backData);
//        GISMap.mapContext.addLayer(backBox, null);
        
//        GISMap.mapContext.addLayer(crestObjects, null);
//        GISMap.mapContext.addLayer(reliefObjects, null)
    }
    
    /*
     * Filter selected data from chosen data layers.
     */
    private void filterData() {
        // get necessary data
        
        for (FeatureSource<SimpleFeatureType, SimpleFeature> bData : bufferData) {
            otherObjects.add(getSelectedObjects(bData));
        }
        
        reliefObjects = getSelectedObjects(reliefData);
        crestObjects = getSelectedObjects(crestData);
    }
    
    /*
     * Get selected features collection from feature source.
     */
    private FeatureCollection<SimpleFeatureType, SimpleFeature> getSelectedObjects(FeatureSource<SimpleFeatureType, SimpleFeature> data) {
        FeatureCollection<SimpleFeatureType, SimpleFeature> cutObjects = FeatureCollections.newCollection();
        SimpleFeature selectedFeature;
        Geometry selectedGeometry;
        Filter filter;
        
        Iterator<?> selectedIterator = GISMap.selectedFeatures.iterator();
        while(selectedIterator.hasNext()) {
            try {
                selectedFeature = (SimpleFeature) selectedIterator.next();
                selectedGeometry = (Geometry) selectedFeature.getDefaultGeometry();
                
                filter = filterFactory.intersects(filterFactory.property(selectedFeature.getDefaultGeometryProperty().getName()), 
                                                  filterFactory.literal(selectedGeometry));
                
                cutObjects.addAll(data.getFeatures(filter));
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        
        return cutObjects;
    }
    
    /*
     * Buffer data from chosen data layers.
     */
    private void bufferData() {
        Iterator<?> iterator;
        SimpleFeature selectedFeature;
        Geometry selectedGeometry;
        Geometry bufferedGeometry;
        
        for (FeatureCollection<SimpleFeatureType, SimpleFeature> otherObject : otherObjects) {
            iterator = otherObject.iterator();
            while(iterator.hasNext()) {
                selectedFeature = (SimpleFeature) iterator.next();
                selectedGeometry = (Geometry) selectedFeature.getDefaultGeometry();
                bufferedGeometry = StaticGeometry.buffer(selectedGeometry, objectBufferization);
                
                selectedFeature.setDefaultGeometry(bufferedGeometry);
            }
        }
    }
    
    /*
     * Merge data.
     */
    private void mergeData() {
        backData = unionData(GISMap.selectedFeatures);
    }
    
    /*
     * Union all data to one feature.
     */
    private SimpleFeature unionData(FeatureCollection<SimpleFeatureType, SimpleFeature> data) {
        
        SimpleFeature feature = null;
        SimpleFeature mergedFeature = null;
        Geometry geometry = null;
        Geometry mergedGeometry = null;
        
        Iterator<?> iterator = data.iterator();
        while(iterator.hasNext()) {
            feature = (SimpleFeature) iterator.next();
            
            if (data.size() == 1) {
                return feature;
            }
            
            geometry = (Geometry) feature.getDefaultGeometry();
            
            if (mergedGeometry == null) {
                mergedGeometry = geometry;
                continue;
            }
            
            mergedGeometry = StaticGeometry.union(mergedGeometry, geometry);
            feature.setDefaultGeometry(mergedGeometry);
            mergedFeature = feature;
        }
        
        return mergedFeature;
    }
    
    private void showCenter() {
        SimpleFeature feature;
        Geometry geometry;
        
        SimpleFeature centerFeature;
        Geometry centerGeometry;
        
        FeatureCollection<SimpleFeatureType, SimpleFeature> featureCollection = FeatureCollections.newCollection();
        
        Iterator<SimpleFeature> iterator = GISMap.selectedFeatures.iterator();
        while(iterator.hasNext()) {
            feature = iterator.next();
            geometry = (Geometry) feature.getDefaultGeometry();
            
            centerGeometry = StaticGeometry.centroid(geometry);
            centerFeature = feature;
            centerFeature.setDefaultGeometry(centerGeometry);
            
            featureCollection.add(centerFeature);
        }
        
        GISMap.mapContext.addLayer(featureCollection, null);
    }
    
    /*
     * Show selected area bounding box.
     */
    private void showBoundingBox() {
        backBox = FeatureCollections.newCollection();
        
        SimpleFeature feature;
        Geometry geometry;
        
        feature = backData;
        geometry = (Geometry) feature.getDefaultGeometry();
        
        geometry = createBox(feature.getBounds().getMinX(), feature.getBounds().getMinY(), feature.getBounds().getMaxX(), feature.getBounds().getMaxY());
        feature.setDefaultGeometry(geometry);
        
        backBox.add(feature);
    }
    
    private void fillWithCircles() {
        FeatureCollection<SimpleFeatureType, SimpleFeature> fc = FeatureCollections.newCollection();
        
        SimpleFeature feature;
        Geometry geometry;
        
        SimpleFeature circleFeature;
        Geometry circleGeometry;
        
        Iterator<SimpleFeature> iterator = backBox.iterator();
        while(iterator.hasNext()) {
            feature = iterator.next();
            geometry = (Geometry) feature.getDefaultGeometry();
            
            Coordinate[] coords = geometry.getCoordinates();
//            for (Coordinate coord : coords) {
//                double x = coord.x;
//                double y = coord.y;
//                
//                System.out.println("x=" + x + ",y=" + y);
//            }
            double sx = coords[0].x;
            double sy = coords[0].y;
            
            System.out.println("x=," + sx + ",y=" + sy);
            
            circleGeometry = createCircle(sx+4000, sy+4000,4000);
            
            SimpleFeatureTypeBuilder typeBuilder = new SimpleFeatureTypeBuilder();
            typeBuilder.setName("Circle #1");
            typeBuilder.add("ID", Integer.class);
            typeBuilder.add("the_geom", Polygon.class);
            SimpleFeatureType featureType = typeBuilder.buildFeatureType();
            
            SimpleFeatureBuilder featureBuilder = new SimpleFeatureBuilder(featureType);
            featureBuilder.add(0);
            featureBuilder.add(circleGeometry);
            SimpleFeature newFeature = featureBuilder.buildFeature("Circle #1");
            fc.add(newFeature);
            
            System.out.println(newFeature);
            
        }
        
        GISMap.mapContext.addLayer(fc, null);
    }
    
    /*
     * Return circle polygon.
     */
    private Geometry createCircle(double x, double y, final double RADIUS) {
        
        GeometryFactory factory = JTSFactoryFinder.getGeometryFactory(null);
        
        final int SIDES = 32;
        Coordinate coords[] = new Coordinate[SIDES + 1];
        for (int i = 0; i < SIDES; i++) {
            double angle = ((double) i / (double) SIDES) * Math.PI * 2.0;
            double dx = Math.cos(angle) * RADIUS;
            double dy = Math.sin(angle) * RADIUS;
            coords[i] = new Coordinate((double) x + dx, (double) y + dy);
        }
        coords[SIDES] = coords[0];
        
        LinearRing ring = factory.createLinearRing(coords);
        Polygon polygon = factory.createPolygon(ring, null);
        
        return ring;
//        return polygon;
    }
    
    /*
     * Return box polygon.
     */
    private Geometry createBox(double lx, double ly, double ux, double uy) {
        
        GeometryFactory factory = JTSFactoryFinder.getGeometryFactory( null );
        
        Coordinate coords[] = new Coordinate[5];
        coords[0] = new Coordinate(lx, ly);
        coords[1] = new Coordinate(ux, ly);
        coords[2] = new Coordinate(ux, uy);
        coords[3] = new Coordinate(lx, uy);
        coords[4] = coords[0];
        
        LinearRing ring = factory.createLinearRing( coords );
        Polygon polygon = factory.createPolygon( ring, null );
        
        return polygon;
    }
    
    private void reverseBufferData() {
        Geometry geometry;
        Geometry bufferedGeometry;
        
        geometry = (Geometry) backData.getDefaultGeometry();
        
        SimpleFeature feature = innerBufferFeature(geometry, towerRange/2);
        showInMap(feature);
        
        circleBuffer(feature);
    }
    
    /*
     * Return inner buffered feature.
     */
    private SimpleFeature innerBufferFeature(Geometry geometry, double range) {
        Geometry bufferedGeometry = StaticGeometry.buffer(geometry, -range);
        
        SimpleFeatureTypeBuilder typeBuilder = new SimpleFeatureTypeBuilder();
        typeBuilder.setName("Inner buffered feature");
        typeBuilder.add("the_geom", Polygon.class);
        SimpleFeatureType featureType = typeBuilder.buildFeatureType();
        
        SimpleFeatureBuilder featureBuilder = new SimpleFeatureBuilder(featureType);
        featureBuilder.add(bufferedGeometry);
        SimpleFeature bufferedFeature = featureBuilder.buildFeature("Shape #1");
        
        return bufferedFeature;
    }
    
    
    private void circleBuffer(SimpleFeature feature) {
        Geometry geometry = (Geometry) feature.getDefaultGeometry();
        
        Geometry circleGeometry;
        
        double x;
        double y;
        for (Coordinate coord : geometry.getCoordinates()) {
            
            x = coord.x;
            y = coord.y;
            
//            circleGeometry = createCircle(x, y, towerRange);
//            showCircle(circleGeometry);
            
            circleGeometry = createCircle(x, y, towerRange*2);
            showCircle(circleGeometry);
            
            Geometry cross = StaticGeometry.intersection(geometry, circleGeometry);
            
            
//            for (Coordinate cord : cross.getCoordinates()) {
//                circleGeometry = createCircle(cord.x, cord.y, 50);
//                showCircle(circleGeometry);
//            }
            
            GeometryFactory factory = JTSFactoryFinder.getGeometryFactory(null);
            LinearRing ring = factory.createLinearRing(geometry.getCoordinates());
            
            Geometry xxx = StaticGeometry.intersection(ring, circleGeometry);
            
            System.out.println(xxx);
            
            Coordinate cord = xxx.getCoordinates()[0];
            
            circleGeometry = createCircle(cord.x, cord.y, 50);
            showCircle(circleGeometry);
            
//            for (Coordinate cord : cross.getCoordinates()) {
//                for (Coordinate tmp : circleGeometry.getCoordinates()) {
//                    if (tmp == cord) {
//                        circleGeometry = createCircle(cord.x, cord.y, towerRange);
//                        showCircle(circleGeometry);
//                    }
//                }
//            }
            
            
            
//            x = cross.getCoordinates()[15].x;
//            y = cross.getCoordinates()[15].y;
            
//            circleGeometry = createCircle(x, y, towerRange);
//            showCircle(circleGeometry);
            
            break;
        }
    }
    
    private void showCircle(Geometry geometry) {
        SimpleFeatureTypeBuilder typeBuilder = new SimpleFeatureTypeBuilder();
        typeBuilder.setName("Circle feature");
        typeBuilder.add("the_geom", Polygon.class);
        SimpleFeatureType featureType = typeBuilder.buildFeatureType();
        
        SimpleFeatureBuilder featureBuilder = new SimpleFeatureBuilder(featureType);
        featureBuilder.add(geometry);
        SimpleFeature circleFeature = featureBuilder.buildFeature("Shape #1");
        
        showInMap(circleFeature);
    }
    
    // additional methods
    
    /*
     * Show simple feature in a map.
     */
    private void showInMap(SimpleFeature feature) {
        FeatureCollection<SimpleFeatureType, SimpleFeature> featureCollection = FeatureCollections.newCollection();
        featureCollection.add(feature);
        GISMap.mapContext.addLayer(featureCollection, null);
    }
    
    // assign local variables
    
    public void setBufferData(ArrayList<FeatureSource<SimpleFeatureType, SimpleFeature>> bufferData) {
        this.bufferData = bufferData;
    }
    
    public void setReliefData(FeatureSource<SimpleFeatureType, SimpleFeature> reliefData) {
        this.reliefData = reliefData;
    }
    
    public void setCrestData(FeatureSource<SimpleFeatureType, SimpleFeature> crestData) {
        this.crestData = crestData;
    }
    
    public void setObjectBufferization(int objectBufferization) {
        this.objectBufferization = objectBufferization;
    }
    
    public void setWirelessCoverage(int wirelessCoverage) {
        this.wirelessCoverage = wirelessCoverage;
    }
    
    public void setTowerHeight(int towerHeight) {
        this.towerHeight = towerHeight;
    }
    
    public void setTowerRange(int towerRange) {
        this.towerRange = towerRange;
    }
    
    // filter
    
    private FilterFactory2 filterFactory = CommonFactoryFinder.getFilterFactory2(null);
    
    // result feature collections
    
    private FeatureCollection<SimpleFeatureType, SimpleFeature> towersData;
    private FeatureCollection<SimpleFeatureType, SimpleFeature> rangeData;
    
    private SimpleFeature backData;
    private FeatureCollection<SimpleFeatureType, SimpleFeature> backBox;
    
    // cropped feature collections
    
    private ArrayList<FeatureCollection<SimpleFeatureType, SimpleFeature>> otherObjects = new ArrayList<FeatureCollection<SimpleFeatureType, SimpleFeature>>();
    private FeatureCollection<SimpleFeatureType, SimpleFeature> reliefObjects;
    private FeatureCollection<SimpleFeatureType, SimpleFeature> crestObjects;
    
    // feature sources
    
    private ArrayList<FeatureSource<SimpleFeatureType, SimpleFeature>> bufferData = new ArrayList<FeatureSource<SimpleFeatureType, SimpleFeature>>();
    private FeatureSource<SimpleFeatureType, SimpleFeature> reliefData;
    private FeatureSource<SimpleFeatureType, SimpleFeature> crestData;
    
    // parameters
    
    private int objectBufferization;
    private int wirelessCoverage;
    private int towerHeight;
    private int towerRange;
}
